/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package uk.ac.liv;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * System utilities for ProteoSuite
 *
 * @author faviel
 */
public class SystemUtils<T> {

    public String getTime() {
        DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
        Date date = new Date();
        return dateFormat.format(date);
    }

    public static void CheckMemory(String sTitle) {
        int mb = 1024 * 1024;
        Runtime runtime = Runtime.getRuntime();
        System.out.println("Memory check at " + sTitle + " ... ");
        System.out.println("Used Memory:" + ((runtime.totalMemory() - runtime.freeMemory()) / mb) + " MB");
        System.out.println("Free Memory:" + (runtime.freeMemory() / mb) + " MB");
        System.out.println("Total Memory:" + (runtime.totalMemory() / mb) + " MB");
        System.out.println("Max Memory:" + (runtime.maxMemory() / mb) + " MB");
    }

    /*
     * http://jvalentino.blogspot.co.uk/2007/02/shortcut-to-calculating-power-set-using.html
     */
    /**
     * Returns the power set from the given set by using a binary counter
     * Example: S = {a,b,c} P(S) = {[], [c], [b], [b, c], [a], [a, c], [a, b],
     * [a, b, c]}
     *
     * @param set String[]
     *
     * @return LinkedHashSet
     */
    public static <T> LinkedHashSet powerset(Set<T> set) {

        //create the empty power set
        LinkedHashSet power = new LinkedHashSet();
        
        ArrayList<T> setList = new ArrayList(set);

        //get the number of elements in the set
        int elements = set.size();

        //the number of members of a power set is 2^n
        int powerElements = (int) Math.pow(2, elements);

        //run a binary counter for the number of power elements
        for (int i = 0; i < powerElements; i++) {

            //convert the binary number to a string containing n digits
            String binary = intToBinary(i, elements);

            //create a new set
            LinkedHashSet innerSet = new LinkedHashSet();

            //convert each digit in the current binary number to the corresponding element
            //in the given set
            for (int j = 0; j < binary.length(); j++) {
                if (binary.charAt(j) == '1') {
                    innerSet.add(setList.get(j));
                }
            }

            //add the new set to the power set
            power.add(innerSet);

        }

        return power;
    }

    /**
     * Returns the sub set of power set from the given set according to the max
     * number of elements in the element sets Example: S = {a,b,c}, max_number =
     * 2 P(S, max_number) = {[], [c], [b], [b, c], [a], [a, c], [a, b]}
     *
     * @param set String[]
     *
     * @return LinkedHashSet
     */
    public static <T> LinkedHashSet powerset(Set<T> set, int maxNum) {
        //check is maxNum bigger than the set.size
        if (maxNum > set.size()) {
            maxNum = set.size();
        }

        //create the empty power set        
        LinkedHashSet power = new LinkedHashSet();

        ArrayList<T> setList = new ArrayList(set);

        //get the number of elements in the set
        int elements = set.size();

        //the number of members of a power set is 2^n
        int powerElements = (int) Math.pow(2, elements);

        //run a binary counter for the number of power elements
        for (int i = 0; i < powerElements; i++) {

            //convert the binary number to a string containing n digits
            String binary = intToBinary(i, elements);

            //check if binary contains more than maxNum of 1
            if (numberOfOne(binary) <= maxNum) {

                //create a new set
                LinkedHashSet innerSet = new LinkedHashSet();

                //convert each digit in the current binary number to the corresponding element
                //in the given set
                for (int j = 0; j < binary.length(); j++) {
                    if (binary.charAt(j) == '1') {
                        innerSet.add(setList.get(j));
                    }
                }

                //add the new set to the power set
                power.add(innerSet);

            }
        }

        return power;
    }

    public static <T> LinkedHashSet powersetx(Set<T> set, int maxNum) {
        //check is maxNum bigger than the set.size
        if (maxNum > set.size()) {
            maxNum = set.size();
        }

        LinkedHashSet power = new LinkedHashSet();

        if (maxNum == 0) {
            LinkedHashSet tempSet = new LinkedHashSet();
            power.add(tempSet);
        }
        else if (maxNum == 1) {
            for (T s : set) {
                LinkedHashSet tempSet = new LinkedHashSet();
                tempSet.add(s);
                power.add(tempSet);
            }
        }
        else {
            power = operator(powersetx(set, maxNum - 1), powersetx(set, 1));
        }

        return power;
    }

    private static LinkedHashSet operator(LinkedHashSet<LinkedHashSet> bigSet,
                                          LinkedHashSet<LinkedHashSet> baseSet) {

        LinkedHashSet power = new LinkedHashSet();
        Iterator iBigSet = bigSet.iterator();
        while (iBigSet.hasNext()) {
            LinkedHashSet bigS = (LinkedHashSet) iBigSet.next();

            Iterator iBaseSet = baseSet.iterator();
            while (iBaseSet.hasNext()) {
                LinkedHashSet baseS = (LinkedHashSet) iBaseSet.next();
                LinkedHashSet tempSet = new LinkedHashSet();
                tempSet.addAll(baseS);
                tempSet.addAll(bigS);
                power.add(tempSet);
            }
        }

        return power;
    }

    private static int numberOfOne(String binary) {
        int n = 0;
        for (int k = 0; k < binary.length(); k++) {
            if (binary.charAt(k) == '1') {
                n++;
            }
        }
        return n;
    }

    /**
     * Converts the given integer to a String representing a binary number with
     * the specified number of digits For example when using 4 digits the binary
     * 1 is 0001
     *
     * @param binary int
     * @param digits int
     *
     * @return String
     */
    private static String intToBinary(int binary, int digits) {

        String temp = Integer.toBinaryString(binary);
        int foundDigits = temp.length();
        String returner = temp;
        for (int i = foundDigits; i < digits; i++) {
            returner = "0" + returner;
        }

        return returner;
    }

}
